﻿/* Copyright (c) 2008, 2009, 2010, 2011, John Cronin
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holder nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace tysila
{
    class Program
    {
        public const string DefaultAssembler = "x86_64";
        public const string DefaultOFormat = "elf64";

        static string output_header = null;
        public static string input_file = null;
        static string output_file = null;
        public static string assembler = DefaultAssembler;
        public static string oformat = DefaultOFormat;
        static string output_cinit = null;
        static string irdump = null;
		static int debug_level = 0;
        static bool interactive = false;
        static bool requested_module_only = false;
        static List<string> extra_types = new List<string>();
        static List<string> extra_methods = new List<string>();
        static List<string> extra_typeinfos = new List<string>();

        /* Boiler plate */
        const string version = "0.2.0";
        const string year = "2009,2010,2011";
        const string authors = "John Cronin";
        const string website = "http://tysos.googlecode.com";
        const string nl = "\n";
        public const string bplate = "tysila " + version + " (" + website + ")" + nl +
            "Copyright (C) " + year + " " + authors + nl +
            "This is free software.  Please see the source for copying conditions.  There is no warranty, " +
            "not even for merchantability or fitness for a particular purpose";

        public static List<string> search_dirs = new List<string> { "", ".", Program.DirectoryDelimiter };

		public static string DirectoryDelimiter
		{
			get
			{
				if(Environment.OSVersion.Platform == PlatformID.Unix)
					return "/";
				else
					return "\\";
			}
		}

        public static Assembler LoadAssembler(string assembler)
        {
            try
            {
                Type asst = Type.GetType("tysila." + assembler + "_Assembler", false, true);
                System.Reflection.ConstructorInfo ctorm = asst.GetConstructor(new Type[] { });
                if (ctorm == null)
                    throw new TypeLoadException();
                return ctorm.Invoke(new object[] { }) as Assembler;
            }
            catch (Exception e)
            {
                if (e is TypeLoadException)
                {
                    Console.WriteLine(assembler + " is not a valid assembler");
                    Console.WriteLine();
                    return null;
                }
                throw;
            }
        }

        static int Main(string[] args)
        {
            if (ParseArgs(args) == false)
            {
                ShowHelp();
                return -1;
            }
            
            Assembler ass = LoadAssembler(assembler);
            if (ass == null)
            {
                ShowHelp();
                return -1;
            }

            Metadata m = Metadata.LoadAssembly(input_file, ass);
            File f = m.File;
            
            if (irdump != null)
                ass.ProduceIrDump = true;

            ass.AddAssembly(m);

            if (interactive)
            {
                Interactive i = new Interactive(ass);
                if (i.Run() == Interactive.InteractiveReturn.Quit)
                    return 0;
            }

#if !DEBUG
            try
            {
#endif
            if (output_file != null)
            {
                /* Find the requested output module */
                IOutputFile ew = null;
                Type[] ass_types = typeof(Assembler).Assembly.GetTypes();
                foreach (Type t in ass_types)
                {
                    if (t.Name.ToLower() == (oformat + "Writer").ToLower())
                    {
                        System.Reflection.ConstructorInfo ctorm2 = t.GetConstructor(new Type[] { typeof(string) });
                        ew = ctorm2.Invoke(new object[] { input_file }) as IOutputFile;
                        break;
                    }
                }
                if (ew == null)
                {
                    ShowHelp();
                    return -1;
                }

                // Add in extra types, methods and typeinfos requested on the command line
                foreach (string e in extra_types)
                {
                    Assembler.TypeToCompile ttc = Mangler.DemangleType(e, ass);
                    ass.Requestor.RequestType(ttc);
                    ass.Requestor.RequestTypeInfo(ttc);

                    Layout l = new Layout(ttc, ass);
                    foreach(Layout.ImplementedInterface.ImplementedInterfaceMember iim in l.Members)
                    {
                        if(iim.ImplementedMangledName != "__cxa_pure_virtual")
                            ass.Requestor.RequestMethod(iim.Implementation);
                    }
                }
                foreach (string e in extra_typeinfos)
                {
                    Assembler.TypeToCompile ttc = Mangler.DemangleType(e, ass);
                    ass.Requestor.RequestTypeInfo(ttc);
                }
                foreach (string e in extra_methods)
                {
                    Assembler.MethodToCompile mtc = Mangler.DemangleMethod(e, ass);
                    ass.Requestor.RequestMethod(mtc);
                }

                // Limit to only compiling types in the assembly listed on the command line, if requested
                if (requested_module_only)
                    ass.Requestor.ModulesToAssemble.Add(m);

                // Find the entry point
                if (f.GetStartToken() != 0x0)
                {
                    Token ept = new Token(f.GetStartToken(), m);
                    Metadata.MethodDefRow mdr = ept.Value as Metadata.MethodDefRow;
                    Metadata.TypeDefRow tdr = Metadata.GetOwningType(mdr.m, mdr);
                    Assembler.MethodToCompile start_point = new Assembler.MethodToCompile
                    {
                        meth = mdr,
                        msig = Signature.ParseMethodDefSig(m, mdr.Signature),
                        type = tdr,
                        tsig = new Signature.Param(new Token(tdr), ass).Type
                    };
                    ass.Requestor.RequestMethod(start_point);
                    ew.SetEntryPoint(Mangler.Mangle(start_point, ass));
                }

                // Identify those methods with the always compile attribute
                foreach (Metadata.CustomAttributeRow car in m.Tables[(int)Metadata.TableId.CustomAttribute])
                {
                    Metadata.MethodDefRow camdr = Metadata.GetMethodDef(car.Type.ToToken(), ass);

                    string caname = Mangler.Mangle(camdr, ass).Substring(Mangler.Mangle(camdr.m).Length);
                    if(caname == "_N3_ABI_T22_AlwaysCompileAttribute_Special_M5_.ctor_S6_200001")
                    {
                        Metadata.MethodDefRow mdr = Metadata.GetMethodDef(car.Parent.ToToken(), ass);
                        Metadata.TypeDefRow tdr = Metadata.GetOwningType(mdr.m, mdr);
                        ass.Requestor.RequestMethod(new Assembler.MethodToCompile
                        {
                            meth = mdr,
                            msig = Signature.ParseMethodDefSig(m, mdr.Signature),
                            type = tdr,
                            tsig = new Signature.Param(new Token(tdr), ass).Type
                        });
                    }
                }

                ass.DoCompile(ew, new Interactive(ass));

                // Add the string tables for the requested modules
                foreach (Assembler.AssemblyInformation ai in ass.GetLoadedAssemblies())
                    ai.m.StringTable.WriteToOutput(ew, ass);


                FileStream oput = new FileStream(output_file, FileMode.Create, FileAccess.Write);
                ew.Write(oput);
                oput.Close();
            }
#if !DEBUG
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: " + e.Message);
				if(debug_level >= 1)
				{
					Console.WriteLine("Exception object: " + e.GetType().ToString());
					Console.WriteLine("Stack trace: \n{0}", e.StackTrace);
				}
                return -1;
            }
#endif

            if ((output_cinit != null) && (output_header == null))
            {
                if (output_cinit.Contains(".c"))
                    output_header = output_cinit.Substring(0, output_cinit.LastIndexOf(".c")) + ".h";
                else
                    output_header = output_cinit + ".h";
            }
            if (output_header != null)
                WriteHeader(m, ass);

            if (irdump != null)
            {
                StreamWriter idsw = new StreamWriter(new FileStream(irdump, FileMode.Create, FileAccess.Write));
                idsw.Write(ass.IrDump);
                idsw.Close();
            }

            return 0;
        }

        private static void WriteHeader(Metadata m, Assembler ass)
        {
            FileStream fs = new FileStream(output_header, FileMode.Create, FileAccess.Write);
            StreamWriter sw = new StreamWriter(fs);

            StreamWriter hmsw = new StreamWriter(new MemoryStream());
            StreamWriter cmsw = new StreamWriter(new MemoryStream());

            StreamWriter oci = null;
            if (output_cinit != null)
            {
                oci = new StreamWriter(new FileStream(output_cinit, FileMode.Create, FileAccess.Write));
                oci.WriteLine("#include \"" + output_header + "\"");
                oci.WriteLine("#include <string.h>");
                oci.WriteLine("#include <malloc.h>");
                oci.WriteLine();
                oci.WriteLine("static int32_t next_obj_id = -1;");
                oci.WriteLine();
            }

            List<string> advance_defines = new List<string>();
            List<string> external_defines = new List<string>();
            List<string> func_headers = new List<string>();

            EmitType(Metadata.GetTypeDef("mscorlib", "System", "Object", ass), hmsw, cmsw, advance_defines,
                external_defines, func_headers, ass);
            EmitType(Metadata.GetTypeDef("mscorlib", "System", "String", ass), hmsw, cmsw, advance_defines,
                external_defines, func_headers, ass);
            EmitArrayInit(hmsw, cmsw, func_headers, ass);

            foreach (Metadata.CustomAttributeRow car in m.Tables[(int)Metadata.TableId.CustomAttribute])
            {
                Metadata.MethodDefRow camdr = Metadata.GetMethodDef(car.Type.ToToken(), ass);

                Mangler.IncludeModule = false;
                string caname = Mangler.Mangle(camdr, ass);
                Mangler.IncludeModule = true;
                if(caname == "_N3_ABI_T22_OutputCHeaderAttribute_Special_M5_.ctor_S6_200001")
                {
                    Metadata.TypeDefRow tdr = Metadata.GetTypeDef(car.Parent.ToToken(), ass);
                    EmitType(tdr, hmsw, cmsw, advance_defines, external_defines, func_headers, ass);
                }
            }

            sw.WriteLine("#include <stdint.h>");
            sw.WriteLine();
            foreach (string s in advance_defines)
                sw.WriteLine(s);
            sw.WriteLine();
            if (oci != null)
            {
                foreach (string s2 in func_headers)
                    sw.WriteLine(s2);
                sw.WriteLine();
            }
            hmsw.Flush();
            StreamReader hmsr = new StreamReader(hmsw.BaseStream);
            hmsr.BaseStream.Seek(0, SeekOrigin.Begin);
            string hs = hmsr.ReadLine();
            while (hs != null)
            {
                sw.WriteLine(hs);
                hs = hmsr.ReadLine();
            }

            sw.Close();

            if (oci != null)
            {
                foreach (string s in external_defines)
                    oci.WriteLine(s);
                oci.WriteLine();

                cmsw.Flush();
                StreamReader cmsr = new StreamReader(cmsw.BaseStream);
                cmsr.BaseStream.Seek(0, SeekOrigin.Begin);
                string cs = cmsr.ReadLine();
                while (cs != null)
                {
                    oci.WriteLine(cs);
                    cs = cmsr.ReadLine();
                }
                oci.Close();
            }
        }

        private static void EmitType(Metadata.TypeDefRow tdr, StreamWriter hmsw, StreamWriter cmsw,
            List<string> advance_defines, List<string> external_defines, List<string> header_funcs, Assembler ass)
        {
            Layout l = new Layout(new Assembler.TypeToCompile { tsig = new Signature.Param(tdr, ass), type = tdr }, ass);
            //Layout l = tdr.GetLayout(new Signature.Param(new Token(tdr), ass).Type, ass, null);

            if (!tdr.IsEnum(ass))
            {
                hmsw.WriteLine("struct " + tdr.TypeNamespace + "_" + tdr.TypeName + " {");
                advance_defines.Add("struct " + tdr.TypeNamespace + "_" + tdr.TypeName + ";");
                foreach (Layout.ImplementedInterface.ImplementedInterfaceField iif in l.Fields)
                    hmsw.WriteLine("    " + ass.GetCType(iif.Field.fsig) + " " + iif.Field.field.Name + ";");
                hmsw.WriteLine("};");
            }
            else
            {
                // Identify underlying type
                string u_type = ass.GetUnderlyingCType(tdr);
                bool needs_comma = false;
                hmsw.WriteLine("enum " + tdr.TypeNamespace + "_" + tdr.TypeName + " {");
                for (Metadata.TableIndex ti = tdr.FieldList; ti < Metadata.GetLastField(tdr.m, tdr); ti++)
                {
                    Metadata.FieldRow vfr = ti.Value as Metadata.FieldRow;
                    if ((vfr.Flags & 0x8050) == 0x8050)
                    {
                        foreach (Metadata.ConstantRow cr in tdr.m.Tables[(int)Metadata.TableId.Constant])
                        {
                            if (cr.Parent.Value == vfr)
                            {
                                int val = ass.FromByteArrayI4(cr.Value);
                                if (needs_comma)
                                    hmsw.WriteLine(",");
                                hmsw.Write("    " + vfr.Name + " = " + val.ToString());
                                needs_comma = true;
                            }
                        }
                    }
                }
                hmsw.WriteLine();
                hmsw.WriteLine("};");
            }

            hmsw.WriteLine();

            if (output_cinit != null)
            {
                if (!tdr.IsValueType(ass))
                {
                    string init_func = "void Init_" + tdr.TypeNamespace + "_" + tdr.TypeName + "(struct " +
                        tdr.TypeNamespace + "_" + tdr.TypeName + " *obj)";
                    cmsw.WriteLine(init_func);
                    header_funcs.Add(init_func + ";");
                    cmsw.WriteLine("{");

                    foreach (Layout.ImplementedInterface.ImplementedInterfaceField iif in l.Fields)
                    {
                        switch (iif.Type)
                        {
                            case Layout.Field.FieldType.ObjectId:
                                cmsw.WriteLine("    obj->" + iif.Field.field.Name + " = next_obj_id--;");
                                break;

                            case Layout.Field.FieldType.VTablePointer:
                                cmsw.WriteLine("    obj->" + iif.Field.field.Name + " = Get_Symbol_Addr(\"" + l.VTableMangledName + "\") + " +
                                    l.GetInterface(new Assembler.TypeToCompile { tsig = iif.Field.definedin_tsig, type = iif.Field.definedin_type }, ass).OffsetWithinVTable.ToString() + ";");

                                string es = ass.GetCType(BaseType_Type.U) + " Get_Symbol_Addr(const char *name);";
                                if (!external_defines.Contains(es))
                                    external_defines.Add(es);

                                break;
                        }
                    }

                    cmsw.WriteLine("}");
                    cmsw.WriteLine();

                    if (tdr.TypeFullName == "System.String")
                        EmitStringInit(tdr, hmsw, cmsw, advance_defines, external_defines, header_funcs, ass);
                }
            }
        }

        private static void EmitArrayInit(StreamWriter hmsw, StreamWriter cmsw, List<string> header_funcs, 
            Assembler ass)
        {
            EmitArrayInit(BaseType_Type.Object, "Ref", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.Byte, "Byte", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.Char, "Char", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.I, "I", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.I1, "I1", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.I2, "I2", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.I4, "I4", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.I8, "I8", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.U, "U", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.U1, "U1", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.U2, "U2", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.U4, "U4", hmsw, cmsw, header_funcs, ass);
            EmitArrayInit(BaseType_Type.U8, "U8", hmsw, cmsw, header_funcs, ass);
        }

        private static void EmitArrayInit(BaseType_Type baseType_Type, string tname, StreamWriter hmsw, StreamWriter cmsw,
            List<string> header_funcs, Assembler ass)
        {
            string typestr = ass.GetCType(baseType_Type);
            string init_func_name = "void Create_" + tname + "_Array(void **arr_obj, " +
                ass.GetCType(BaseType_Type.I) + " num_elems)";

            /* void Create_Ref_Array(void **arr_obj, native_int num_elems)
             * size = sizeof(native_int) + num_elems * sizeof(elem_type)
             */

            header_funcs.Add(init_func_name + ";");
            cmsw.WriteLine(init_func_name);
            cmsw.WriteLine("{");
            cmsw.WriteLine("    *arr_obj = malloc(sizeof(" + ass.GetCType(BaseType_Type.I) + ") + num_elems * " +
                "sizeof(" + typestr + "));");
            cmsw.WriteLine("    *((" + ass.GetCType(BaseType_Type.I) + " *)*arr_obj) = num_elems;");
            cmsw.WriteLine("}");
            cmsw.WriteLine();

            string get_addr_name = typestr + " * Get_" + tname + "_Elem(void *arr_obj, " +
                ass.GetCType(BaseType_Type.I) + " idx)";
            
            /* elem_type* Get_Ref_Elem(void *arr_obj, native_int idx)
             * get item at index
             * 
             * unsigned char * p = (uint_8 *)arr_obj;
             * unsigned char * q = p + sizeof(native_int);
             * typestr * t = (elem_type *)q;
             * ret = &q[idx]
             *
             * -> ret = &(((elem_type *)(((uint_8 *)arr_obj) + sizeof(native_int)))[idx])
             */

            header_funcs.Add(get_addr_name + ";");
            cmsw.WriteLine(get_addr_name);
            cmsw.WriteLine("{");
            cmsw.WriteLine("    return &(((" + typestr + "*)(((uint8_t *)arr_obj) + sizeof(" +
                ass.GetCType(BaseType_Type.I) + ")))[idx]);");
            cmsw.WriteLine("}");
            cmsw.WriteLine();
        }

        private static void EmitStringInit(Metadata.TypeDefRow tdr, StreamWriter hmsw, StreamWriter cmsw,
            List<string> advance_defines, List<string> external_defines, List<string> header_funcs, Assembler ass)
        {
            // Emit a string creation instruction of the form:
            // void CreateString(System_String **obj, const char *s)

            string init_func = "void CreateString(struct System_String **obj, const char *s)";
            header_funcs.Add(init_func + ";");

            cmsw.WriteLine(init_func);
            cmsw.WriteLine("{");
            cmsw.WriteLine("    int l = strlen(s);");
            cmsw.WriteLine("    int i;");
            cmsw.WriteLine("    " + ass.GetCType(BaseType_Type.Char) + " *p;");
            cmsw.WriteLine("    *obj = (struct System_String *)malloc(sizeof(struct System_String) + l * sizeof(" +
                ass.GetCType(BaseType_Type.Char) + "));");
            cmsw.WriteLine("    Init_System_String(*obj);");
            cmsw.WriteLine("    (*obj)->length = l;");
            cmsw.WriteLine("    p = (" + ass.GetCType(BaseType_Type.Char) +
                " *)(*obj + sizeof(struct System_String));");
            cmsw.WriteLine("    for(i = 0; i < l; i++)");
            cmsw.WriteLine("        p[i] = (" + ass.GetCType(BaseType_Type.Char) + ")s[i];");
            cmsw.WriteLine("}");
            cmsw.WriteLine();
        }

        private static bool ParseArgs(string[] args)
        {
            int i = 0;
            while (i < args.Length)
            {
                if (args[i] == "--oformat")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    oformat = args[i];
                }
                else if (args[i] == "-o")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    output_file = args[i];
                }
				else if (args[i] == "-d")
				{
					debug_level++;
				}
                else if (args[i] == "--assembler")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    output_file = args[i];
                }
                else if (args[i] == "--output-header")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    output_header = args[i];
                }
                else if (args[i] == "--output-cinit")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    output_cinit = args[i];
                }
                else if (args[i] == "--irdump")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    irdump = args[i];
                }
                else if (args[i] == "--requested-module-only")
                {
                    requested_module_only = true;
                }
                else if (args[i].StartsWith("-L"))
                {
                    if (args[i].Length > 2)
                        Program.search_dirs.Add(args[i].Substring(2));
                    else
                    {
                        i++;
                        Program.search_dirs.Add(args[i]);
                    }
                }
                else if (args[i] == "--extra-type")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    extra_types.Add(args[i]);
                }
                else if (args[i] == "--extra-typeinfo")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    extra_typeinfos.Add(args[i]);
                }
                else if (args[i] == "--extra-method")
                {
                    i++;
                    if ((i >= args.Length) || (args[i].StartsWith("-")))
                        return false;
                    extra_methods.Add(args[i]);
                }
                else if (args[i] == "-i")
                {
                    interactive = true;
                }
                else if (args[i].StartsWith("-"))
                    return false;
                else
                {
                    input_file = args[i];
                    FileInfo fi = new FileInfo(input_file);
                    search_dirs.Add(fi.DirectoryName);
                }
                i++;
            }
            if (input_file == null)
                return false;
            return true;
        }

        static string[] GetSupportedArchitectures()
        {
            List<string> ret = new List<string>();

            Type[] ass_types = typeof(Assembler).Assembly.GetTypes();
            foreach (Type t in ass_types)
            {
                if (t.Name.ToLower().EndsWith("_assembler"))
                {
                    if ((t.IsSubclassOf(typeof(Assembler))) && (!t.IsAbstract))
                        ret.Add(t.Name.ToLower().Substring(0, t.Name.ToLower().LastIndexOf("_assembler")));
                }
            }

            return ret.ToArray();
        }
        public static string GetSupportedArchitecturesList()
        {
            StringBuilder sb = new StringBuilder();
            string[] a = GetSupportedArchitectures();
            for (int i = 0; i < a.Length; i++)
            {
                sb.Append(a[i]);
                if (i < (a.Length - 1))
                    sb.Append(", ");
            }
            return sb.ToString();
        }

        static string[] GetSupportedOutputFormats()
        {
            List<string> ret = new List<string>();

            Type[] ass_types = typeof(Assembler).Assembly.GetTypes();
            foreach (Type t in ass_types)
            {
                if (t.Name.ToLower().EndsWith("writer"))
                {
                    List<Type> ints = new List<Type>(t.GetInterfaces());
                    if (ints.Contains(typeof(IOutputFile)) && (!t.IsAbstract))
                        ret.Add(t.Name.ToLower().Substring(0, t.Name.ToLower().LastIndexOf("writer")));
                }
            }

            return ret.ToArray();
        }

        public static string GetSupportedOutputFormatsList()
        {
            StringBuilder sb = new StringBuilder();

            string[] of = GetSupportedOutputFormats();
            for (int i = 0; i < of.Length; i++)
            {
                sb.Append(of[i]);
                if (i < (of.Length - 1))
                    sb.Append(", ");
            }
            return sb.ToString();
        }

        static void ShowHelp()
        {
            Console.WriteLine(bplate);
            Console.WriteLine();
            string cmd_line = Environment.GetCommandLineArgs()[0];
            FileInfo fi = new FileInfo(cmd_line);
            Console.WriteLine("Usage: " + fi.Name + " [options] input_file");
            Console.WriteLine();
            Console.WriteLine("    -o output_file");
            Console.WriteLine("    --oformat output_format");
            Console.WriteLine("    --assembler architecture");
            Console.WriteLine("    --output-header name  output a C-like header for certain types");
            Console.WriteLine("                           as marked by the ABI.OutputCHeader attribute");
            Console.WriteLine("    --output-cinit name   output a C file to initialize certain types");
            Console.WriteLine("                           implies --output-header");
            Console.WriteLine("    --irdump irdump_file  dump the intermediate representation to a file");
            Console.WriteLine("    --L dir               add dir to the directory search list for");
            Console.WriteLine("                           references");
			Console.WriteLine("    -d                    increase debug level");
            Console.WriteLine("    -i                    start in interactive mode");
            Console.WriteLine("    --requested-module-only    only compile items in the current module");
            Console.WriteLine();
            Console.Write("Supported output formats: ");
            Console.WriteLine(GetSupportedOutputFormatsList());
            Console.Write("Supported architectures: ");
            Console.WriteLine(GetSupportedArchitecturesList());
            Console.WriteLine();
            Console.WriteLine();
        }
    }
}
